#ifndef _LIB_KERNEL_MEMORY_H_
#define _LIB_KERNEL_MEMORY_H_

#include "bitmap.h"
#include "list.h"
#include "stdint.h"
#include "sync.h"

/************************** 位图地址 *******************************************
 * 因为 0xc009f000 是内核主线程栈顶, 0xc009e000 是内核主线程的pcb(pcb占用1页 =
 *4k). 一个页框大小的位图可表示128M内存, 位图位置安排在地址0xc009a000,
 * 这样本系统最大支持4个页框的位图, 即512M
 ******************************************************************************/
#define MEM_BITMAP_BASE 0xc009a000

#define USR_VADDR_BITMAP_BYTE_LEN (0xc0000000 - USER_VADDR_START) / PG_SIZE / 8

/* 0xc0000000是内核从虚拟地址3G起. 0x100000意指跨过低端1M内存,
 * 使虚拟地址在逻辑上连续 */
#define K_HEAP_START 0xc0100000

#define PG_P_1 1
#define PG_P_0 0

#define PG_RW_R 0
#define PG_RW_W 2

#define PG_US_S 0
#define PG_US_U 4

#define DESC_CNT 7

extern struct pool kernel_pool, user_pool;

enum pool_flags
{
  kernel_pflag = 1,
  user_pflag = 2
};

/**
 * @brief memory pool
 * @details manage memory pages with physical address
 */
struct pool
{
  /**
   * @brief start physical address of first memory page
   */
  uint32_t phy_address_start;
  /**
   * @brief count of memory pages managed
   */
  uint32_t pool_size;
  /**
   * @brief status of memory page allocation
   */
  struct bitmap pool_bitmap;

  /**
   * @brief mutex lock for memory alloc
   */
  struct mutex_t lock;
};

/**
 * @brief virtual address
 */
struct virtual_addr
{
  /**
   * @brief start of virtual address
   */
  uint32_t vaddr_start;
  /**
   * @brief status of virtual memory allocation
   */
  struct bitmap vaddr_bitmap;
};

struct mem_block
{
  struct list_elem free_elem;
};

struct mem_block_desc
{
  uint32_t    each_block_size;
  uint32_t    blocks_cnt_per_arena;
  struct list free_block_list;
};

struct arena
{
  struct mem_block_desc* desc;
  bool                   is_large_block;
  uint32_t               free_mem_cnt;
};

/**
 * @brief initialize memory management system
 */
void mem_init();

/**
 * @brief Allocates a block of memory pages from kernel memory pool.
 *
 * This function requests a contiguous block of physical memory pages from
 * kernel memory pool, where the size of the block is specified by the parameter
 * pg_cnt.
 *
 * @param pg_cnt The number of pages to allocate.
 *
 * @return A pointer to the starting virtual address of the allocated memory
 * block if the allocation is successful; NULL if the allocation fails.
 */
void* kernel_page_alloc(uint32_t pg_cnt);

/**
 * @brief Allocates a block of memory pages from user memory pool.
 *
 * This function requests a contiguous block of physical memory pages from
 * user memory pool, where the size of the block is specified by the parameter
 * pg_cnt.
 *
 * @param pg_cnt The number of pages to allocate.
 *
 * @return A pointer to the starting virtual address of the allocated memory
 * block if the allocation is successful; NULL if the allocation fails.
 */
void* user_page_alloc(uint32_t pg_cnt);

/**
 * @brief Allocates a block of memory consisting of a specified number of pages.
 *
 * This function allocates a block of memory, consisting of 'pg_cnt' consecutive
 * pages, from the specified memory pool (kernel or user). It performs the
 * following steps:
 *
 * 1. Requests a virtual address range for the memory block.
 * 2. Maps the allocated physical pages to the allocated virtual address range.
 *
 * @param pf       Pool flags indicating whether the allocation is for the
 * kernel or user.
 * @param pg_cnt   The number of consecutive pages to allocate.
 *
 * @return A pointer to the starting virtual address of the allocated memory
 * block if successful; NULL if the allocation fails.
 */
void* physical_page_alloc(enum pool_flags pf, uint32_t pg_cnt);

/**
 * @brief transform virtual_addr to physical_addr
 *
 * @param vaddr virtual_addr
 *
 * @return physical_addr
 */
uint32_t addr_v2p(uint32_t vaddr);

/**
 * @brief Initialize memory block descriptors.
 *
 * @param desc_array Pointer to an array of memory block descriptors.
 *
 * Initializes memory block descriptors with different block sizes.
 * The block sizes start from 16 bytes and double in size for each descriptor,
 * until reaching 1024 bytes.
 * Calculates the number of blocks per arena for each descriptor and initializes
 * the doubly linked list for free memory block.
 */
void block_desc_init(struct mem_block_desc* desc_array);

/**
 * @brief System call function to allocate memory from memory pool.
 *
 * This function can be invoked as a system call from both user and kernel space
 * to allocate memory of arbitrary sizes from the user or kernel memory pool.
 *
 * @param size Size of the memory block to allocate.
 * @return Pointer to the allocated memory block, or NULL if allocation fails.
 */
void* sys_malloc(uint32_t size);

/**
 * @brief System call function to release memory from memory pool.
 *
 * This function releases a memory block allocated by the memory manager. It
 * determines whether the block is from the kernel or user memory pool, and then
 * performs the necessary cleanup, including releasing the virtual memory and
 * possibly physical memory if the block is a large block exceeding 1024 bytes.
 *
 * @param ptr Pointer to the memory block to be freed.
 */
void sys_free(void* ptr);

#endif
